// #include "byteblock.h"
#include "thermistor.h"
#include "pixelbuffer.h"
#include "max7219.h"
#include "settings.h"




double read_avg(int rounds=50, int drop=0) {
  unsigned long result = 0;

  while (drop-- > 0) {
    analogRead(SensorPin);
  }

  if (rounds == 0) {
    return 0.0;
  }
  
  digitalWrite(PowerPin, HIGH);  // Power the sensor subcircuit
  delay(SensorPowerOnDelay);  // wait until smoothing-capacitor is fully charged

  for (int i=0; i<rounds; i++) {
    result += analogRead(SensorPin);
  }

  digitalWrite(PowerPin, LOW);  // Power down the sensor subcircuit

  unsigned int tmp = result % 1024;
  result /= rounds;

  return (double)result + ((double)tmp / 1024.0);
}


// Sample for a certain period in milliseconds. 
//Choosing a value of 100 (ms) for read_for_ms should cancel out 50Hz and 60Hz noise
double read_avg_for_ms(unsigned int read_for_ms=SampleTime, unsigned int drop_rounds=0) {

  for (unsigned int i=0; i<drop_rounds; i++)
    analogRead(SensorPin);

  unsigned long result = 0;
  unsigned long rounds = 0;

  if (read_for_ms == 0) {
    return 0.0;
  }

  digitalWrite(PowerPin, HIGH);  // Power the sensor subcircuit
  delay(SensorPowerOnDelay);  // wait until smoothing-capacitor is fully charged

  unsigned long now = millis();
  unsigned long read_until = now + read_for_ms;  // TODO: Write Checks for Overflow
  
  do {
    for (uint8_t i=0; i<10; i++) {
      result += analogRead(SensorPin);
    } 
    rounds += 10;
    now = millis();
  // (now > 2) so that things work even if millis() starts again from 0
  } while (now < read_until);
  
  digitalWrite(PowerPin, LOW);  // Power down the sensor subcircuit

  unsigned int tmp = result % 1024;
  result /= rounds;

  return (double)result + ((double)tmp / 1024.0);
}


double get_sensor_voltage(void) {
  double adc_units = read_avg_for_ms(SampleTime);
  double measured_voltage = adc_units * mv_per_adc_unit;

  #ifdef DEBUG
    // Serial.println(String("Measured Voltage: ") + String(measured_voltage) + String(" Raw: ") + adc_units);
  #endif

  // definitions of Voltage References and stuff can be looked up here: 
  // https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/avr/variants/tinyx5/pins_arduino.h
  return measured_voltage;
}


PixelBuffer pb_temp = PixelBuffer();
PixelBuffer pb_plot = PixelBuffer();

Max7219 max72(pb_temp);



void sieg_heil(void) {
  const byte SiegHeil[] = {0, 0, 0, 0, 78, 219, 219, 114, 
                          0, 195, 255, 195, 0, 255, 219, 219, 
                          195, 0, 126, 195, 219, 123, 0, 0, 
                          0, 255, 24, 24, 255, 0, 255, 219, 
                          219, 195, 0, 195, 255, 195, 0, 255, 
                          192, 192, 224, 0, 223, 223, 0, 0, 
                          0, 0};

  for (uint8_t i=0; i<sizeof(SiegHeil); i++) {
    pb_temp.move_pixelbuffer_left(SiegHeil[i]);
    max72.show();
    delay(25);
  }
  for (uint8_t i=0; i<sizeof(SiegHeil)-8; i++) {
    pb_temp.move_pixelbuffer_left(0x00);
    max72.show();
    delay(25);
  }
}

void heil_hitler(void) {
  const byte Heil[] = {0, 0, 0, 0, 0, 0, 0, 255, 
                      24, 24, 255, 0, 255, 219, 219, 195, 
                      0, 195, 255, 195, 0, 255, 192, 192, 
                      192, 0, 0, 0, 0, 0, 0, 0,};

  const byte Hitler[] = {0, 0, 255, 24, 24, 255, 0, 195, 
                        255, 195, 0, 7, 195, 255, 195, 7, 
                        0, 255, 192, 192, 224, 0, 255, 219, 
                        219, 195, 0, 255, 27, 59, 238, 0};
  
  PixelBuffer *old_pb = max72.get_pixelbuffer();
  max72.set_pixelbuffer(pb_temp);

  pb_temp.set_pixelbuffer(Heil, sizeof(Heil));
  max72.show();
  delay(400);

  pb_temp.clear_pixelbuffer();
  max72.show();
  delay(300);

  pb_temp.set_pixelbuffer(Hitler, sizeof(Hitler));
  max72.show();
  delay(700);

  pb_temp.clear_pixelbuffer();
  max72.show();
  delay(600);

  max72.set_pixelbuffer(*old_pb);
}

void update_temperature(double temperature, bool show_degrees = true) {
  pb_temp.set_float(temperature);
  if (show_degrees) {
    pb_temp.push_letter('D');
    if(UseFahrenheit)
      pb_temp.push_letter('F');
    else
      pb_temp.push_letter('C');
  }
  pb_temp.draw_pixelbuffer(true);
  pb_temp.center_pixelbuffer();
}

void setup() {
  #ifdef ARDUINO_AVR_NANO
    #ifdef DEBUG
      Serial.begin(38400);
      Serial.println("Running Tests:");
      //test_byteblock();
      //test_thermistor();
      //test_pixelbuffer();
      // test_max_setup();
    #endif
  #endif

  
  // production code
  pinMode(PowerPin, OUTPUT);
  digitalWrite(PowerPin, LOW);

  #ifdef ARDUINO_AVR_NANO
      // Board is Nano
    analogReference(INTERNAL);
  #else
  // Board must be ATtiny85
  // Todo: should use 2.56V reference, but it's broken
    analogReference(INTERNAL1V1);
  #endif

  read_avg(0, 1000);  // Dropping 1000 rounds of readings, because  the ADC reference voltare has changed

  // heil_hitler();
  sieg_heil();

}

void loop() {
  #ifdef DEBUG
    // test_max_loop();
  #endif

  // Measuring Starting Time and determining start-time of next loop
  // TODO: This can be prettified
  unsigned long start_time = millis();

  // Initializing Thermistor
  static Thermistor therm(ReferenceVoltage,  // Reference Voltage
                          R1,  // R1 in Ohm
                          Beta,  // Beta
                          RBeta,  // R_Beta in Ohm
                          RBetaTemp);  // Temperature of R_Beta Measurement in Degree Celsius
  
  double measured_voltage = get_sensor_voltage();  // Measure Voltage
  double temperature = therm.calc_temp(measured_voltage, UseFahrenheit);  // Calculate Temperature from Voltage

  // Initialize min and max temperature with initial temperature
  static double min_temp = temperature;
  static double max_temp = temperature;

  // Counters
  static unsigned int plot_switch_counter = 0;  // counts to ShowTempDelayCount or ShowPlotDelayCount to switch the display-mode between temperature and plot
  static unsigned int plot_update_counter = 0;  // counts to PlotUpdateCount to add another plot-row
  static uint8_t plot_cols = 0;  // counts the number of already displayed columns on the plot

  // switches
  static bool display_plot = false;  // signifies whether the plot is being displayed
  bool switched = false;  // will change to true if display modes are being switched, so that plot_switch_counter can either be reset ot incremented

  long wait_time = (MainloopTimePeriodMS / 2) - (millis() - start_time);
  if (wait_time<0) wait_time = 0;
  delay(wait_time);

  start_time = millis();

  // make sure that min and max temperature vars are updated
  if (temperature < min_temp)
    min_temp = temperature;
  if (temperature > max_temp)
    max_temp = temperature;
  
  if (display_plot) {
    // currently displaying Plot
    if (plot_switch_counter >= ShowPlotDelayCount) {
      max72.set_pixelbuffer(pb_temp);  // Changing Pixelbuffer to Temperature Buffer

      display_plot = false;
      switched = true;
    }
  }
  else {
    // currently displaying Temperature

    if (plot_switch_counter >= ShowTempDelayCount) {
      if (plot_cols >= MinPlotColsBeforeSwitching) {
        max72.set_pixelbuffer(pb_plot);  // Changing Pixelbuffer to Plot Buffer
        display_plot = true;
      }
      switched = true;
    }
  }

  if (switched) {
    plot_switch_counter = 0;
  }
  else {
    plot_switch_counter++;
  }
  
  if (plot_update_counter >= PlotUpdateCount) {
    pb_plot.insert_stat_column(min_temp, max_temp, PlotStepPerPixel);

    if(plot_cols <= MinPlotColsBeforeSwitching) {
      plot_cols++;
    }
    plot_update_counter = 0;
    min_temp = temperature;
    max_temp = temperature;
  }
  else {
    plot_update_counter++;
  }

  if (!display_plot)
    update_temperature(temperature);
  max72.show();

  wait_time = (MainloopTimePeriodMS / 2) - (millis() - start_time);
  if (wait_time<0) wait_time = 0;
  delay(wait_time);
}

// TODO:
